package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
	_ "github.com/mattn/go-sqlite3"
	"time"
	"github.com/gin-contrib/sessions"
	"github.com/go-xorm/xorm"
	"strconv"
	"fmt"
	"net"
	"encoding/gob"
	"go/types"
	"github.com/spf13/viper"
	"github.com/swaggo/gin-swagger"
	"github.com/swaggo/gin-swagger/swaggerFiles"
	_ "UserManager/docs"
)

type CustomGin struct {
	*gin.Engine
	startTime int64
}

type CustomServer struct {
	http.Server
}

type User struct {
	Id        int    `json:"id" form:"id" xorm:"pk 'id'"`
	Email     string `json:"email" form:"email" xorm:"'email'" `
	FirstName string `json:"firstName" form:"firstName" xorm:"'firstName'" `
	LastName  string `json:"lastName" form:"lastName" xorm:"'lastName'" `
	Password  string `json:"password" form:"password" xorm:"'password'" `
	Status    int    `json:"status" form:"status" xorm:"'status'" comment:"1:未激活;2:激活;3:删除"`
}

type omit *struct{}

type PublicUser struct {
	User
	Password omit `json:"password,omitempty"`
}

const SessionKey string = "userId"

var dbEngine *xorm.Engine

func init() {
	gin.SetMode(gin.ReleaseMode)
	gob.Register(types.Int)
}

// @title UserManager API
// @version 1.0
// @summary UserManager后端API接口
// @description UserManager后端API接口

// @contact.name piaohao
// @contact.url https://gitee.com/how-to-do-it-in-gin/UserManager
// @contact.email piao.hao@qq.com

// @BasePath /api/v1
func main() {
	dbEngine, _ = xorm.NewEngine("sqlite3", "db/user.db")
	engine := &CustomGin{
		Engine:    gin.Default(),
		startTime: time.Now().UnixNano(),
	}
	engine.Use(NewUtil().CORSMiddleware())
	engine.Use(NewUtil().SessionMiddleware())
	engine.Use(NewUtil().ErrorMiddleware())
	{
		group := engine.Group("/api/v1")
		group.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
		group.POST("/sign_up", signUp)
		group.POST("/login", login)
		group.POST("/active", active)
		group.GET("/logout", logout).Use(NewUtil().AuthMiddleware())
		group.DELETE("/sessions", logout)
		group.GET("/users", users)
		group.GET("/users/:userId", user)
		group.GET("/my/profile", profile)
		group.POST("/users", addUser)
		group.PUT("/users", updateUser)
		group.DELETE("/users/:userId", deleteUser)
	}
	engine.CustomRun(":" + viper.GetString("server.port"))
}

func (g *CustomGin) CustomRun(address string) (err error) {
	defer func() { fmt.Printf("服务器启动异常:%s", err) }()
	server := &CustomServer{}
	server.Addr = address
	server.Handler = g
	return server.ListenAndServe()
}

func (srv *CustomServer) ListenAndServe() error {
	addr := srv.Addr
	if addr == "" {
		addr = ":http"
	}
	ln, err := net.Listen("tcp", addr)
	if err != nil {
		return err
	}
	fmt.Printf("系统启动成功,监听端口:%s,耗时:%.3f毫秒\n", viper.GetString("server.port"),
		(float64(time.Now().UnixNano())-float64(srv.Handler.(*CustomGin).startTime))/1e6)
	return srv.Serve(ln)
}

// @Summary 注册
// @Description 注册账户
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   email     query    string     true        "邮箱"
// @Param   firstName     query    string     true        "FirstName"
// @Param   lastName     query    string     true        "LastName"
// @Success 200 {string} json	"{"message":"success"}"
// @Failure 400 {string} string "Invalid email"
// @Failure 409 {string} string "Invalid exists"
// @Failure 500 {string} string "系统内部错误"
// @Router /sign_up [post]
func signUp(c *gin.Context) {
	var user User
	c.ShouldBind(&user)
	if !NewUtil().IsEmail(user.Email) {
		c.String(http.StatusBadRequest, "Invalid email")
		return
	}
	if exist, _ := dbEngine.Where("email=?", user.Email).Exist(&User{}); exist {
		c.String(http.StatusConflict, "Invalid exists")
		return
	}
	if _, err := dbEngine.Exec("insert into user (email,firstName,lastName,status) values (?,?,?,?)",
		user.Email, user.FirstName, user.LastName, 1); err != nil {
		c.Error(err)
		return
	}
	go func() {
		if err := NewUtil().SendMail(user.Email); err != nil {
			fmt.Printf("发送邮件错误:%s", err)
		}
	}()
	c.JSON(http.StatusOK, gin.H{"message": "success"})
}

type ActiveModel struct {
	Token    string `form:"token"`
	Password string `form:"password"`
}

// @Summary 激活账号接口
// @Description 激活账号接口
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   token     query    string     true        "邮件带过来的加密串"
// @Param   password     query    string     true        "密码"
// @Success 200 {string} json	"{"message":"active success"}"
// @Failure 400 {string} string "Invalid email"
// @Failure 404 {string} string "user not exist"
// @Failure 404 {string} string "account has been actived"
// @Failure 500 {string} string "系统内部错误"
// @Router /active [post]
func active(c *gin.Context) {
	var activeModel ActiveModel
	c.ShouldBind(&activeModel)
	encryptEmail := activeModel.Token
	if encryptEmail == "" {
		c.String(http.StatusBadRequest, "Invalid email")
		return
	}
	var user User
	if exist, _ := dbEngine.Where("email=?", encryptEmail).Get(&user); !exist {
		c.String(http.StatusNotFound, "user not exist")
		return
	}
	if user.Password != "" {
		c.String(http.StatusNotFound, "account has been actived")
		return
	}
	user.Password = NewUtil().Encrypt(activeModel.Password)
	if _, err := dbEngine.Exec("update user set password=?,status=2 where email=? and status=1",
		user.Password, user.Email); err != nil {
		c.Error(err)
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "active success"})
}

// @Summary 登录
// @Description 登录
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   email     query    string     true        "邮箱"
// @Param   password     query    string     true        "密码"
// @Success 201 {string} json	"{"message":"login success"}"
// @Failure 400 {string} string "Invalid email"
// @Failure 400 {string} string "Invalid password"
// @Failure 409 {string} string "user not exist or password is not right"
// @Failure 500 {string} string "系统内部错误"
// @Router /login [post]
func login(c *gin.Context) {
	var user User
	c.ShouldBind(&user)
	if !NewUtil().IsEmail(user.Email) {
		c.String(http.StatusBadRequest, "Invalid email")
		return
	}
	if user.Password == "" {
		c.String(http.StatusBadRequest, "Invalid password")
		return
	}
	var record User
	if exist, _ := dbEngine.Where("email=? and password=? and status=2", user.Email, NewUtil().Encrypt(user.Password)).Get(&record); !exist {
		c.String(http.StatusConflict, "user not exist or password is not right")
		return
	}
	session := sessions.Default(c)
	session.Set(SessionKey, record.Id)
	session.Save()
	c.JSON(http.StatusCreated, gin.H{"message": "login success"})
}

// @Summary 退出
// @Description 退出
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   email     query    string     true        "邮箱"
// @Param   password     query    string     true        "密码"
// @Success 204 {string} json	"{"message":"logout success"}"
// @Failure 500 {string} string "系统内部错误"
// @Router /logout [post]
// @Router /sessions [delete]
func logout(c *gin.Context) {
	session := sessions.Default(c)
	session.Delete(SessionKey)
	session.Save()
	c.JSON(http.StatusNoContent, gin.H{"message": "logout success"})
}

// @Summary 用户列表
// @Description 用户列表
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   q     query    string     true        "查询条件"
// @Param   page     query    int     true        "第几页"
// @Param   pageSize     query    int     true        "每页大小"
// @Success 200 {string} json	"{"data": users, "recordsTotal": total, "recordsFiltered": total, "draw": 1}"
// @Failure 400 {string} string "Invalid page"
// @Failure 500 {string} string "系统内部错误"
// @Router /users [get]
func users(c *gin.Context) {
	q := c.Query("q")
	pageNoStr := c.Query("page")
	pageSizeStr := c.Query("pageSize")
	pageSize, err := strconv.Atoi(pageSizeStr)
	page, err := strconv.Atoi(pageNoStr)
	if err != nil || page < 0 {
		c.String(http.StatusBadRequest, "Invalid page")
		return
	}
	dbSession := dbEngine.NewSession()
	if q != "" {
		dbSession = dbEngine.Where("firstName like ? or lastName like ?", "%"+q+"%", "%"+q+"%")
	}
	var users []User
	dbSession.Limit(pageSize, page*pageSize).Find(&users)
	total, _ := dbEngine.Count(&User{})
	filteredCount := 0
	if users != nil {
		filteredCount = len(users)
	} else {
		users = []User{}
	}
	//password过滤操作
	var publicUsers []PublicUser
	for _, u := range users {
		publicUsers = append(publicUsers, PublicUser{User: u})
	}
	c.JSON(http.StatusOK, gin.H{"data": publicUsers, "recordsTotal": filteredCount, "recordsFiltered": total})
}

// @Summary 用户profile
// @Description 用户profile
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   userId     path    int     true        "用户id"
// @Success 200 {string} json	"{"user": user}"
// @Failure 500 {string} string "系统内部错误"
// @Router /users/{userId} [get]
func user(c *gin.Context) {
	var user User
	dbEngine.ID(c.Param("userId")).Get(&user)
	c.JSON(http.StatusOK, gin.H{"user": PublicUser{User: user},})
}

// @Summary 用户profile
// @Description 用户profile
// @Produce  json
// @Success 200 {string} json	"{"user": user}"
// @Failure 500 {string} string "系统内部错误"
// @Router /my/profile [get]
func profile(c *gin.Context) {
	var user User
	dbEngine.ID(sessions.Default(c).Get(SessionKey)).Get(&user)
	c.JSON(http.StatusOK, gin.H{"user": PublicUser{User: user},})
}

// @Summary 增加用户信息
// @Description 增加用户信息
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   email     query    string     true        "邮箱"
// @Param   firstName     query    string     true        "firstName"
// @Param   lastName     query    int     true        "lastName"
// @Param   password     query    int     true        "密码"
// @Success 201 {string} json	"{"message": "add success"}"
// @Failure 409 {string} string "user exist"
// @Failure 500 {string} string "系统内部错误"
// @Router /users [post]
func addUser(c *gin.Context) {
	var user User
	c.ShouldBind(&user)
	if exist, _ := dbEngine.Where("email=?", NewUtil().Decrypt(user.Email)).Exist(&User{}); exist {
		c.String(http.StatusConflict, "user exist")
		return
	}
	if _, err := dbEngine.Exec("insert into user (email,firstName,lastName,password,status) values (?,?,?,?,?)",
		user.Email, user.FirstName, user.LastName, NewUtil().Encrypt(user.Password), 2); err != nil {
		c.Error(err)
		return
	}
	c.JSON(http.StatusCreated, gin.H{"message": "add success",})
}

// @Summary 更新用户信息
// @Description 更新用户信息
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   id     query    int     true        "用户id"
// @Param   firstName     query    string     true        "firstName"
// @Param   lastName     query    int     true        "lastName"
// @Param   password     query    int     true        "密码"
// @Success 204 {string} json	"{"message": "update success"}"
// @Failure 400 {string} string "user not exist"
// @Failure 500 {string} string "系统内部错误"
// @Router /users [put]
func updateUser(c *gin.Context) {
	var user User
	c.ShouldBind(&user)
	if exist, _ := dbEngine.ID(user.Id).Exist(&User{}); !exist {
		c.String(http.StatusBadRequest, "user not exist")
		return
	}
	user.Password = NewUtil().Encrypt(user.Password)
	dbEngine.Id(user.Id).Omit("email").Update(&user)
	c.JSON(http.StatusNoContent, gin.H{"message": "update success",})
}

// @Summary 删除用户
// @Description 删除用户
// @Accept   application/x-www-form-urlencoded
// @Produce  json
// @Param   userId     path    int     true        "用户id"
// @Success 204 {string} json	"{"message": "delete success"}"
// @Failure 400 {string} string "user not exist"
// @Failure 500 {string} string "系统内部错误"
// @Router /users/{userId} [delete]
func deleteUser(c *gin.Context) {
	userId := c.Param("userId")
	if exist, _ := dbEngine.Id(userId).Exist(&User{}); !exist {
		c.String(http.StatusBadRequest, "user not exist")
		return
	}
	dbEngine.Exec("update user set status=3 where id=?", userId)
	c.JSON(http.StatusNoContent, gin.H{"message": "delete success",})
}
